package jstudy.gu.kit.http;

import java.io.BufferedReader;
/**
 * @author guweichao
 * @created 2021年1月30日
 * @desc jdk8 调用http请求
 */
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import jstudy.gu.user.User;

import jstudy.gu.kit.string.Joiner;
import jstudy.gu.kit.string.JsonKit;
import jstudy.gu.kit.string.StringKit;

public class SimpleHttp {

	public static void main(String[] args) {
		SimpleHttp sh = new SimpleHttp(0);
		sh.param("name", "aa").getUrl("http://localhost:8090/user/getAll");
		HttpResult<List<User>, List<User>> result = sh.doHttp(ofUser(StandardCharsets.UTF_8));
		System.out.println(result.getCode());
		int size = result.getResult().size();
		System.out.println(size);
		result.getResult().forEach(System.out::println);
	}

	public static IHttpResultParser<List<User>> ofUser(Charset charset) {
		IHttpResultParser<List<User>> hr = in -> {
			String str = new BufferedReader(new InputStreamReader(in, charset)).lines()
					.collect(Collectors.joining(System.lineSeparator()));
			List<User> beanList = JsonKit.toBeanList(str,User.class);
			return beanList;
		};
		return hr;
	}

	private int timeout; // 超时,
	private String url; // 请求的url
	private String method; // 请求方法. get post put delete header//都是大写
	private Map<String, Object> params = null; // ?a=1&b=2
	private Map<String, String> headers = null; // 请求中的header
	private Map<String, String> requestProperties = null; //
	private Map<String, File> uploads = null;
	private Object body;

	// 上传
	private String boundary = "***SimpleHttpClientBoundary***";
	private String prefix = "--";
	private String end = "\r\n";

	public SimpleHttp() {
		this(15000); // 15 s 超时
	}

	public SimpleHttp(int timeout) {
		this.timeout = timeout;
	}

	public SimpleHttp requestProperty(String key, String value) {
		if (requestProperties == null) {
			requestProperties = new HashMap<>();
		}
		requestProperties.put(key, value);
		return this;
	}

	public SimpleHttp header(String key, String value) {
		if (headers == null) {
			headers = new HashMap<>();
		}
		headers.put(key, value);
		return this;
	}

	public SimpleHttp header(Map<String, String> headers) {
		if (headers == null || headers.isEmpty()) {
			return this;
		}
		if (this.headers == null || this.headers.isEmpty()) {
			this.headers = headers;
		} else {
			this.headers.putAll(headers);
		}
		return this;
	}

	public SimpleHttp upload(String key, File file) {
		if (uploads == null) {
			uploads = new HashMap<>();
		}
		uploads.put(key, file);
		return this;
	}

	public SimpleHttp body(Object o) {
		this.body = o;
		return this;
	}

	public SimpleHttp upload(Map<String, File> files) {
		if (files == null || files.isEmpty()) {
			return this;
		}
		if (this.uploads == null || this.uploads.isEmpty()) {
			this.uploads = files;
		} else {
			this.uploads.putAll(files);
		}
		return this;
	}

	public SimpleHttp param(String key, Object value) {
		if (params == null) {
			params = new HashMap<>();
		}
		params.put(key, value);
		return this;
	}

	public SimpleHttp param(Map<String, Object> params) {
		if (params == null || params.isEmpty()) {
			return this;
		}
		if (this.params == null) {
			this.params = new HashMap<>();
		}
		this.params.putAll(params);
		return this;
	}

	public SimpleHttp url(String url, String method) {
		Objects.requireNonNull(method, "method is null");
		this.url = url;
		this.method = method.toUpperCase();
		return this;
	}

	public SimpleHttp getUrl(String url) {
		this.url = url;
		this.method = "GET";
		return this;
	}

	public SimpleHttp postUrl(String url) {
		this.url = url;
		this.method = "POST";
		return this;
	}

	public <T, E> HttpResult<T, E> doHttp(IHttpResultParser<T> resultParser, IHttpResultParser<E> errorParser) {
		Objects.requireNonNull(resultParser, "resultParser 不能为 null");// 检查resultParser
		HttpURLConnection conn = null;
		HttpResult<T, E> hr = null;
		try {
			if (url == null) {
				throw new NullPointerException("url is null");
			}
			URL connurl = new URL(urlWithParams(url, params));
			conn = (HttpURLConnection) connurl.openConnection();
			conn.setConnectTimeout(timeout);
			conn.setReadTimeout(timeout);
			conn.setUseCaches(false);// use cache
			conn.setRequestProperty("Charset", "UTF-8");
			conn.setRequestProperty("Connection", "Keep-Alive");
			conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
			final HttpURLConnection conn1 = conn;
			if (requestProperties != null && !requestProperties.isEmpty()) {
				requestProperties.forEach((k, v) -> conn1.setRequestProperty(k, v));
			}
			if (method != null) {
				conn.setRequestMethod(method);
				conn.setDoInput(true);// post请求需要的, get你可以不设置
				conn.setDoOutput(true);
			}
			if (headers != null && !headers.isEmpty()) {
				headers.forEach((k, v) -> conn1.setRequestProperty(k, v));
			}
			if (body != null) {
				conn.getOutputStream().write(JsonKit.toJson(body).getBytes());
			}

			if (uploads != null && !uploads.isEmpty()) {
				conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
				try (DataOutputStream ds = new DataOutputStream(conn.getOutputStream());) {
					for (Map.Entry<String, File> entry : uploads.entrySet()) {
						ds.writeBytes(prefix + boundary + end);
						String name = entry.getKey();
						File file = entry.getValue();
						String filename = file.getName();
						ds.writeBytes("Content-Disposition: form-data; " + "name=\"" + name + "\";filename=\""
								+ filename + "\"");
						ds.writeBytes(end);
						ds.writeBytes(end);
						try (FileInputStream fStream = new FileInputStream(file);) {
							int bufferSize = 1024;
							byte[] buffer = new byte[bufferSize];
							int length = -1;
							while ((length = fStream.read(buffer)) != -1) {
								ds.write(buffer, 0, length);
							}
							ds.writeBytes(end);
						} catch (Exception e) {
							E ex = null;
							if (errorParser != null) {
								InputStream inputStream = new ByteArrayInputStream(
										StringKit.parseException(e).getBytes());
								ex = errorParser.parse(inputStream);
							}
							hr = new HttpResult<T, E>(null, ex, 0, false);
							return hr;
						}

					}
					ds.writeBytes(prefix + boundary + prefix + end);
					ds.flush();
				} catch (Exception e) {
					E ex = null;
					if (errorParser != null) {
						InputStream inputStream = new ByteArrayInputStream(StringKit.parseException(e).getBytes());
						ex = errorParser.parse(inputStream);
					}
					hr = new HttpResult<T, E>(null, ex, 0, false);
					return hr;
				}
			}
			conn.connect();
			int code = conn.getResponseCode();
			boolean rc = code / 100 == 2;// code 2xx 系列表示正常返回.3xx重定向 4xx客户端错误 5xx 服务端异常
			InputStream in = rc ? conn.getInputStream() : conn.getErrorStream();
			T result = null;
			E exs = null;
			if (in != null) {
				if (rc) {
					result = resultParser.parse(in);
				} else {
					if (errorParser != null) {
						exs = errorParser.parse(in);
					}
				}
			}
			hr = new HttpResult<T, E>(result, exs, code, rc);
			return hr;
		} catch (Exception e) {
			E ex = null;
			if (errorParser != null) {
				InputStream inputStream = new ByteArrayInputStream(StringKit.parseException(e).getBytes());
				ex = errorParser.parse(inputStream);
			}
			hr = new HttpResult<T, E>(null, ex, 0, false);
			return hr;
		} finally {
			if (conn != null) {
				try {
					conn.disconnect();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
		}
	}

	public <T> HttpResult<T, T> doHttp(IHttpResultParser<T> resultParser) {
		return doHttp(resultParser, resultParser);
	}

	private static final Charset defaultCharset = StandardCharsets.UTF_8;

	private static String urlWithParams(String url, Map<?, ?> params) {
		if (params != null && params.size() > 0) {
			Joiner urlparams = new Joiner("&");
			params.forEach((k, v) -> {
				urlparams.add(encode(k) + "=" + encode(v));
			});
			return url + "?" + urlparams.toString();
		}
		return url;
	}

	public static String encode(Object str) {
		return encode(StringKit.of(str));
	}

	public static String encode(String str) {
		try {
			return URLEncoder.encode(str, defaultCharset.name()).replace("+", "%20");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}

}
